﻿using Castle.Core;
using Castle.Core.Logging;
using System.Collections.Concurrent;
using System.Runtime.Remoting.Messaging;
using Yto.Common;
using Yto.Common.Dependency;

namespace Yto.Unitwork
{
    public class CallContextCurrentUnit : ICurrentUnit, ITransient
    {
        public CallContextCurrentUnit()
        {
            Logger = NullLogger.Instance;
        }
        [DoNotWire]
        public IUnitwork Current { get => GetUnit(); set => SetUnit(value,Logger); }

        public ILogger Logger { get; set; }

        private static void SetUnit(IUnitwork value,ILogger logger)
        {
            if (value == null)
            {
                ReleaseCurrentUnit(logger);
                return;
            }
            var unitworkKey = CallContext.LogicalGetData(KEY) as string;
            if (unitworkKey != null)
            {

            }

            unitworkKey = value.Id;
            if (!uwDictionary.TryAdd(unitworkKey, value))
            {
                throw new BaseException("uwDictionary.TryAdd fails!");
            }
            CallContext.LogicalSetData(KEY, unitworkKey);
        }

        private static void ReleaseCurrentUnit(ILogger logger)
        {
            var unitworkKey = CallContext.LogicalGetData(KEY) as string;
            if (unitworkKey == null)
            {
                logger.Warn("There is no uw needs to exit!");
                return;
            }
            IUnitwork unitwork;
            if (!uwDictionary.TryGetValue(unitworkKey, out unitwork))
            {
                CallContext.FreeNamedDataSlot(KEY);
                return;
            }
            uwDictionary.TryRemove(unitworkKey, out unitwork);
        }

        private const string KEY = "com.ytocargo";
        private static readonly ConcurrentDictionary<string, IUnitwork> uwDictionary
            = new ConcurrentDictionary<string, IUnitwork>();

        private static IUnitwork GetUnit()
        {
            var unitworkKey = CallContext.LogicalGetData(KEY) as string;
            if (unitworkKey == null)
            {
                return null;
            }
            if (!uwDictionary.TryGetValue(unitworkKey, out IUnitwork unitwork))
            {
                CallContext.FreeNamedDataSlot(KEY);
                return null;
            }

            if (unitwork.IsDisposed)
            {
                uwDictionary.TryGetValue(unitworkKey, out unitwork);
                CallContext.FreeNamedDataSlot(KEY);
                return null;
            }
            return unitwork;
        }
    }
}
